﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;


//用system.drawing.color 替代
//public struct Color32
//{
//    public Color32(byte r = 255, byte g = 255, byte b = 255, byte a = 255)
//    {
//        this.r = r;
//        this.g = g;
//        this.b = b;
//        this.a = a;
//    }
//    public byte r;
//    public byte g;
//    public byte b;
//    public byte a;
//    public Color ToColor()
//    {
//        return Color.FromArgb(a, r, g, b);
//    }
//}
public struct ColorF
{
    public Color32 ToColor32()
    {
        int ir = (int)(R * 255);
        int ig = (int)(G * 255);
        int ib = (int)(B * 255);
        int ia = (int)(A * 255);
        if (ir < 0) ir = 0;
        if (ir > 255) ir = 255;
        if (ig < 0) ig = 0;
        if (ig > 255) ig = 255;
        if (ib < 0) ib = 0;
        if (ib > 255) ib = 255;
        if (ia < 0) ia = 0;
        if (ia > 255) ia = 255;
        return Color32.FromArgb((byte)ia, (byte)ir, (byte)ig, (byte)ib);
    }
    public ColorF(float r = 1.0f, float g = 1.0f, float b = 1.0f, float a = 1.0f)
    {
        this.R = r;
        this.G = g;
        this.B = b;
        this.A = a;
    }
    public float R;
    public float G;
    public float B;
    public float A;

    public static ColorF White
    {
        get
        {
            return ColorF.FromRGBA(1, 1, 1, 1);
        }
    }
    public static ColorF Red
    {
        get
        {
            return ColorF.FromRGBA(1, 0, 0, 1);
        }
    }
    public static ColorF Black
    {
        get
        {
            return ColorF.FromRGBA(0, 0, 0, 1);
        }
    }
    public static void ToHSV(ColorF c, out int h_degree, out float s, out float v)
    {
        float min, max;

        min = c.R < c.G ? c.R : c.G;
        min = min < c.B ? min : c.B;
        max = c.R > c.G ? c.R : c.G;
        max = max > c.B ? max : c.B;

        // H
        h_degree = 0;
        if (max == min)
        {
            h_degree = 0;
        }
        else if (max == c.R && c.R > c.B)
        {
            h_degree = (int)(60 * (c.G - c.B) * 1.0f / (max - min) + 0);
        }
        else if (max == c.R && c.G < c.B)
        {
            h_degree = (int)(60 * (c.G - c.B) * 1.0f / (max - min) + 360);
        }
        else if (max == c.G)
        {
            h_degree = (int)(60 * (c.B - c.R) * 1.0f / (max - min) + 120);
        }
        else if (max == c.B)
        {
            h_degree = (int)(60 * (c.R - c.G) * 1.0f / (max - min) + 240);
        }
        // S
        if (max == 0)
        {
            s = 0;
        }
        else
        {
            s = (max - min) * 1.0f / max;
        }
        // V
        v = max;
    }
    public static ColorF FromHSV(int h_degree, float s = 1.0f, float v = 1.0f)
    {
        ColorF c = ColorF.White;
        if (s == 0)
            c.R = c.G = c.B = v;
        else
        {
            float h = h_degree / 60.0f;


            int i = (int)h % 6;
            float C = h - i;

            float X = v * (1 - s);
            float Y = v * (1 - s * C);
            float Z = v * (1 - s * (1 - C));
            switch (i)
            {
                case 0: c.R = v; c.G = Z; c.B = X; break;
                case 1: c.R = Y; c.G = v; c.B = X; break;
                case 2: c.R = X; c.G = v; c.B = Z; break;
                case 3: c.R = X; c.G = Y; c.B = v; break;
                case 4: c.R = Z; c.G = X; c.B = v; break;
                case 5: c.R = v; c.G = X; c.B = Y; break;
            }
        }
        return c;
    }

    public static ColorF FromH(int h360)//输入一个0到360的值
    {
        ColorF c = ColorF.White;
        //h360 = 60
        //sin插值
        var v = (h360 % 120 / 120.0f);
        if (v < 0) v += 1;

        var f = MathF.Sin(v * MathF.PI) * 0.5f;
        if (v > 0.5)
            f = 1 - f;
        //直接插值
        //let f = h360 % 120 / 120;
        if (h360 < 120)  //b->r
        {

            if (f <= 0.5)
            {
                c.B = 1.0f;
                c.R = (f) / (1 - f);
            }
            else
            {
                c.R = 1.0f;
                c.B = (1 - f) / f;
            }
            c.G = 0;
        }
        else if (h360 < 240) //r->g
        {

            if (f <= 0.5)
            {
                c.R = 1.0f;
                c.G = 1.0f * f / (1 - f);
            }
            else
            {
                c.G = 1.0f;
                c.R = 1.0f * (1 - f) / f;
            }
            c.B = 0;

        }
        else //g->b
        {

            c.R = 0;
            if (f <= 0.5)
            {
                c.G = 1.0f;
                c.B = 1.0f * f / (1 - f);
            }
            else
            {
                c.B = 1.0f;
                c.G = 1.0f * (1 - f) / f;
            }
        }
        return c;
    }
    public void SetV(float v)
    {
        if (v <= 1.0)
        {
            this.R *= (v);
            this.G *= (v);
            this.B *= (v);
        }
        else
        {

            var maxr = v - 1;
            this.R = this.R * (1 - maxr) + maxr;
            this.G = this.G * (1 - maxr) + maxr;
            this.B = this.B * (1 - maxr) + maxr;
        }
    }
    public static ColorF FromColor32(System.Drawing.Color c)
    {
        return ColorF.FromRGBA(c.R / 255.0f, c.G / 255.0f, c.B / 255.0f, c.A / 255.0f);
    }
    public static ColorF FromRGBA(float r, float g, float b, float a = 1.0f)
    {
        return new ColorF() { R = r, G = g, B = b, A = a };
    }
    public static ColorF FromARGB(float a, float r, float g, float b)
    {
        return new ColorF() { R = r, G = g, B = b, A = a };
    }
}
public struct Color32
{
    public Color32(byte r=255,byte g=255,byte b=255,byte a=255)
    {
        this.R=r;
        this.G=g;
        this.B=b;
        this.A=a;
    }
    public static Color32 FromArgb(byte a,byte r,byte g,byte b)
    {
        Color32 c;
        c.R = r;
        c.G = g;
        c.B = b;
        c.A = a;
        return c;
    }
    public static Color32 White
    {
        get
        {
            return new Color32(255,255,255,255);
        }
    }
    public byte R;
    public byte G;
    public byte B;
    public byte A;
}

public struct Color_Lab
{
    public float l;
    public float a;
    public float b;

    const float Xn = 0.950456f;
    const float Yn = 1.0f;
    const float Zn = 1.088754f;
    public void FromRGB(float r, float g, float b)
    {
        r = gamma(r);
        g = gamma(g);
        b = gamma(b);

        float X = 0.4124564f * r + 0.3575761f * g + 0.1804375f * b;
        float Y = 0.2126729f * r + 0.7151522f * g + 0.0721750f * b;
        float Z = 0.0193339f * r + 0.1191920f * g + 0.9503041f * b;
        float fX, fY, fZ;
        X /= (Xn);
        Y /= (Yn);
        Z /= (Zn);
        if (Y > 0.008856)
        {
            fY = MathF.Pow(Y, 1.0f / 3.0f);

        }
        else
        {
            fY = 7.787f * Y + 16.0f / 116.0f;
        }
        if (X > 0.008856)
            fX = MathF.Pow(X, 1.0f / 3.0f);
        else
            fX = 7.787f * X + 16.0f / 116.0f;

        if (Z > 0.008856)
            fZ = MathF.Pow(Z, 1.0f / 3.0f);
        else
            fZ = 7.787f * Z + 16.0f / 116.0f;
        this.l = (116.0f * Y - 16.0f);
        //亮度保留负值会有更好的比对效果
        //this.l = l > 0.0f ? l : 0.0f;
        this.a = (fX - fY) * 500;
        this.b = (fY - fZ) * 200;
    }
    //    static gamma(x: number) : number {
    //    return x > 0.04045 ? Math.pow((x + 0.055) / 1.055, 2.4) : (x / 12.92);
    //};
    static float gamma(float x)
    {
        return x > 0.04045 ? MathF.Pow((x + 0.055f) / 1.055f, 2.4f) : (x / 12.92f);
    }
    //float gamma(float x)
    //{
    //    return x > 0.04045 ? powf((x + 0.055f) / 1.055f, 2.4f) : (x / 12.92);
    //};

    void RGB2XYZ(byte R, byte G, byte B, ref float X, ref float Y, ref float Z)
    {
        float RR = gamma(R / 255.0f);
        float GG = gamma(G / 255.0f);
        float BB = gamma(B / 255.0f);

        X = 0.4124564f * RR + 0.3575761f * GG + 0.1804375f * BB;
        Y = 0.2126729f * RR + 0.7151522f * GG + 0.0721750f * BB;
        Z = 0.0193339f * RR + 0.1191920f * GG + 0.9503041f * BB;
    }



    void XYZ2Lab(float X, float Y, float Z)
    {
        float fX, fY, fZ;

        X /= (Xn);
        Y /= (Yn);
        Z /= (Zn);

        if (Y > 0.008856f)
            fY = MathF.Pow(Y, 1.0f / 3.0f);
        else
            fY = 7.787f * Y + 16.0f / 116.0f;

        if (X > 0.008856f)
            fX = MathF.Pow(X, 1.0f / 3.0f);
        else
            fX = 7.787f * X + 16.0f / 116.0f;

        if (Z > 0.008856)
            fZ = MathF.Pow(Z, 1.0f / 3.0f);
        else
            fZ = 7.787f * Z + 16.0f / 116.0f;

        this.l = 116.0f * fY - 16.0f;
        this.l = l > 0.0f ? l : 0.0f;
        a = 500.0f * (fX - fY);
        b = 200.0f * (fY - fZ);
    }


    public static float QuickDist(Color_Lab a, Color_Lab b)
    {
        var L = (a.l - b.l);
        var A = (a.a - b.a);
        var B = (a.b - b.b);
        return MathF.Sqrt(
            L * L + A * A + B * B
        );
    }
    static float degrees(float n) { return n * (180.0f / MathF.PI); }
    static float radians(float n) { return n * (MathF.PI / 180.0f); }

    static float hp_f(float x, float y) //(7)
    {
        if (x == 0 && y == 0)
            return 0;
        else
        {
            var tmphp = degrees(MathF.Atan2(x, y));
            if (tmphp >= 0)
                return tmphp;
            else
                return tmphp + 360;
        }
    }

    static float dhp_f(float C1, float C2, float h1p, float h2p) //(10)
    {
        if (C1 * C2 == 0) return 0;
        else if (Math.Abs(h2p - h1p) <= 180) return h2p - h1p;
        else if ((h2p - h1p) > 180) return (h2p - h1p) - 360;
        else if ((h2p - h1p) < -180) return (h2p - h1p) + 360;
        else throw (new Exception());
    }

    static float a_hp_f(float C1, float C2, float h1p, float h2p)
    { //(14)
        if (C1 * C2 == 0) return h1p + h2p;
        else if (Math.Abs(h1p - h2p) <= 180) return (h1p + h2p) / 2.0f;
        else if ((Math.Abs(h1p - h2p) > 180) && ((h1p + h2p) < 360)) return (h1p + h2p + 360) / 2.0f;
        else if ((Math.Abs(h1p - h2p) > 180) && ((h1p + h2p) >= 360)) return (h1p + h2p - 360) / 2.0f;
        else throw (new Exception());
    }
    public static float ciede2000(Color_Lab c1, Color_Lab c2)
    {
        /**
         * Implemented as in "The CIEDE2000 Color-Difference Formula:
         * Implementation Notes, Supplementary Test Data, and Mathematical Observations"
         * by Gaurav Sharma, Wencheng Wu and Edul N. Dalal.
         */

        // Get L,a,b values for color 1
        var L1 = c1.l;
        var a1 = c1.a;
        var b1 = c1.b;

        // Get L,a,b values for color 2
        var L2 = c2.l;
        var a2 = c2.a;
        var b2 = c2.b;

        // Weight factors
        var kL = 1;
        var kC = 1;
        var kH = 1;

        /**
         * Step 1: Calculate C1p, C2p, h1p, h2p
         */
        var C1 = MathF.Sqrt(MathF.Pow(a1, 2) + MathF.Pow(b1, 2)); //(2)
        var C2 = MathF.Sqrt(MathF.Pow(a2, 2) + MathF.Pow(b2, 2)); //(2)

        var a_C1_C2 = (C1 + C2) / 2.0f;             //(3)

        var G = 0.5f * (1 - MathF.Sqrt(MathF.Pow(a_C1_C2, 7.0f) /
            (MathF.Pow(a_C1_C2, 7.0f) + MathF.Pow(25.0f, 7.0f)))); //(4)

        var a1p = (1.0f + G) * a1; //(5)
        var a2p = (1.0f + G) * a2; //(5)

        var C1p = (float)MathF.Sqrt((float)MathF.Pow(a1p, 2) + MathF.Pow(b1, 2)); //(6)
        var C2p = (float)MathF.Sqrt((float)MathF.Pow(a2p, 2) + MathF.Pow(b2, 2)); //(6)

        var h1p = hp_f(b1, a1p); //(7)
        var h2p = hp_f(b2, a2p); //(7)

        /**
         * Step 2: Calculate dLp, dCp, dHp
         */
        var dLp = L2 - L1; //(8)
        var dCp = C2p - C1p; //(9)

        var dhp = dhp_f(C1, C2, h1p, h2p); //(10)
        var dHp = 2 * MathF.Sqrt(C1p * C2p) * MathF.Sin(radians(dhp) / 2.0f); //(11)

        /**
         * Step 3: Calculate CIEDE2000 Color-Difference
         */
        var a_L = (L1 + L2) / 2.0f; //(12)
        var a_Cp = (C1p + C2p) / 2.0f; //(13)

        var a_hp = a_hp_f(C1, C2, h1p, h2p); //(14)
        var T = 1 - 0.17f * MathF.Cos(radians(a_hp - 30)) + 0.24f * MathF.Cos(radians(2 * a_hp)) +
            0.32f * MathF.Cos(radians(3 * a_hp + 6)) - 0.20f * MathF.Cos(radians(4 * a_hp - 63)); //(15)
        var d_ro = 30 * MathF.Exp(-(MathF.Pow((a_hp - 275) / 25, 2))); //(16)
        var RC = MathF.Sqrt((MathF.Pow(a_Cp, 7.0f)) / (MathF.Pow(a_Cp, 7.0f) + MathF.Pow(25.0f, 7.0f)));//(17)
        var SL = 1 + ((0.015f * MathF.Pow(a_L - 50, 2)) /
             MathF.Sqrt(20 + MathF.Pow(a_L - 50, 2.0f)));//(18)
        var SC = 1 + 0.045f * a_Cp;//(19)
        var SH = 1 + 0.015f * a_Cp * T;//(20)
        var RT = -2 * RC * MathF.Sin(radians(2 * d_ro));//(21)
        var dE = MathF.Sqrt(MathF.Pow(dLp / (SL * kL), 2) + MathF.Pow(dCp / (SC * kC), 2) +
            MathF.Pow(dHp / (SH * kH), 2) + RT * (dCp / (SC * kC)) *
            (dHp / (SH * kH))); //(22)

        return dE;
    }

}

